<?php
/*
    ainitramfs common script
    Copyright (C) Ambroz Bizjak, 2010

    This file is part of ainitramfs.

    ainitramfs is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2
    as published by the Free Software Foundation.

    ainitramfs is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

function ai_scripts_common ($extra_dirs, $extra_files, $extra_symlinks, $extra_binaries, $extra_kmod_dirs, $extra_kmods)
{
    global $ai_root;
    global $ai_out;
    global $ai_kmoddir;
    global $ai_kmodroot;
    global $ai_dataroot;
    global $ai_kmods;
    global $ai_options;
    global $ai_toolchain;

    $options = array(
        "ata" => 1,
        "usb" => 1,
        "input" => 1,
        "device_mapper" => 0,
        "debug_tools" => 1,
    );

    // read options
    if (array_key_exists("common", $ai_options)) {
        foreach ($ai_options["common"] as $name => $val) {
            if (!array_key_exists($name, $options)) {
                ai_error("unknown common option ${name}");
            }

            if ($val == "yes") {
                $options[$name] = 1;
            }
            else if ($val == "no") {
                $options[$name] = 0;
            }
            else {
                ai_error("unknown value for common option ${name}");
            }
        }
    }

    // accept options
    unset($ai_options["common"]);

    $dirs = array(
        "/dev",
        "/proc",
        "/sys",
    );
    $dirs = array_merge($dirs, $extra_dirs);

    $files = $extra_files;
    $symlinks = $extra_symlinks;

    $binaries = array(
        "bash",
        "sh",
        "mount",
        "mknod",
        "lsmod",
        "sleep",
        "pidof",
        "mkdir",
        array("/sbin/udevd", "/lib/udev/udevd", "/usr/lib/systemd/systemd-udevd"),
        "udevadm",
        "modprobe",
        "/sbin/modprobe",
        "insmod",
        "blkid",
        "switch_root",
        "stat",
        "depmod",
    );
    $binaries = array_merge($binaries, $extra_binaries);

    $program_paths = array("/usr/sbin", "/usr/bin", "/sbin", "/bin");

    $devices = array(
        array("console", 'c', 5, 1, 0600),
        array("null", 'c', 1, 3, 0666),
    );

    $kmods = array(
        "ext2",
        "ext3",
        "ext4",
        "reiserfs",
        "sd_mod",
    );
    $kmods = array_merge($kmods, $extra_kmods);

    $kmod_dirs = $extra_kmod_dirs;

    // udev search directories
    $udev_dirs = array("/usr/lib/udev", "/lib/udev");

    // find primary udev directory
    $udev_dir = NULL;
    foreach ($udev_dirs as $this_udev_dir) {
        if (is_file(ai_join_paths($ai_root, $this_udev_dir, "rules.d/80-drivers.rules"))) {
            $udev_dir = $this_udev_dir;
            break;
        }
    }
    if ($udev_dir === NULL) {
        ai_error("could not find primary udev directory");
    }

    // udev rules
    $udev_rules = array(
        "50-udev-default.rules",
        "60-persistent-storage.rules",
        "80-drivers.rules",
    );

    if ($options["ata"]) {
        $kmod_dirs = array_merge($kmod_dirs, array(
            "kernel/drivers/ata",
        ));
    }

    $usb_mods = array("ehci-hcd", "ehci-pci", "ohci-hcd", "uhci-hcd", "xhci-hcd");

    if ($options["usb"]) {
        $kmods = array_merge($kmods, $usb_mods, array("usb-storage"));
    }

    if ($options["input"]) {
        $kmods = array_merge($kmods, $usb_mods, array(
            "evdev", "psmouse", "hid", "hid-generic", "usbhid"
        ));
    }

    if ($options["device_mapper"]) {
        $udev_rules = array_merge($udev_rules, array(
            "10-dm.rules",
            "13-dm-disk.rules",
            "95-dm-notify.rules",
        ));

        $binaries = array_merge($binaries, array("dmsetup", "dmeventd"));
        $kmods = array_merge($kmods, array("dm-mod"));
    }

    if ($options["debug_tools"]) {
        $binaries = array_merge($binaries, array(
            "ls",
            "cat",
            "find",
            "dmesg",
            "ps",
            "pstree",
            "top",
        ));
    }

    // install udev rules
    ai_install_fs_dir(ai_join_paths($udev_dir, "rules.d"));
    foreach ($udev_rules as $rule_name) {
        $rule_path = NULL;
        foreach ($udev_dirs as $this_udev_dir) {
            $this_rule_path = ai_join_paths($this_udev_dir, "rules.d", $rule_name);
            if (is_file(ai_join_paths($ai_root, $this_rule_path))) {
                $rule_path = $this_rule_path;
                break;
            }
        }
        if ($rule_path === NULL) {
            ai_error("could not find udev rule file {$rule_name}");
        }
        if (!copy(ai_join_paths($ai_root, $rule_path), ai_join_paths($ai_out, $udev_dir, "rules.d", $rule_name))) {
            ai_error("could not copy udev rule file {$rule_name}");
        }
    }

    // install directories
    foreach ($dirs as $dir) {
        ai_install_fs_dir($dir);
    }

    // install regular files
    foreach ($files as $file) {
        ai_install_fs_file($file);
    }

    // install symlinks
    foreach ($symlinks as $symlink) {
        $sym_file = $symlink[0];
        $sym_target = $symlink[1];
        ai_install_fs_dir(ai_dirname($sym_file));
        $sym_absfile = ai_join_paths($ai_out, $sym_file);
        if (!symlink($sym_target, $sym_absfile)) {
            ai_error("failed to create symlink $sym_absfile => $sym_target");
        }
    }

    // install binaries
    foreach ($binaries as $opts) {
        if (!is_array($opts) && $opts[0] != "/") {
            $arr = array();
            foreach ($program_paths as $path) {
                $arr[] = "{$path}/{$opts}";
            }
            $opts = $arr;
        }

        if (!is_array($opts)) {
            $opts = array($opts);
        }
        $done = FALSE;
        foreach ($opts as $binary) {
            if (file_exists(ai_join_paths($ai_root, $binary))) {
                ai_install_fs_file($binary, $target);
                ai_install_libs_for_binary(ai_join_paths($ai_root, $target));
                $done = TRUE;
                break;
            }
        }

        if (!$done) {
            ai_error("failed to find " . print_r($opts, true));
        }
    }

    // create device nodes
    foreach ($devices as $device) {
        $dev = ai_join_paths($ai_out, "dev", $device[0]);
        $maj = $device[2];
        $min = $device[3];
        $perm = $device[4];
        if ($device[1] == 'c') {
            $mode = POSIX_S_IFCHR;
        }
        else if ($device[1] == 'b') {
            $mode = POSIX_S_IFBLK;
        }
        else {
            ai_error("unknown device type: {$device[1]}");
        }
        if (!posix_mknod($dev, $mode | $perm, $maj, $min)) {
            ai_error("mknod failed: $dev");
        }
    }

    // install single kernel modules
    foreach ($kmods as $kmod) {
        if (!array_key_exists($kmod, $ai_kmods)) {
            ai_warning("unknown kernel module $kmod");
        } else {
            ai_install_kmod($kmod);
        }
    }

    function install_kmod_recurser ($kmod_dir) {
        global $ai_root;
        global $ai_kmoddir;
        global $ai_kmodroot;

        $dir_full = ai_join_paths($ai_kmodroot, $ai_kmoddir, $kmod_dir);
        if (!is_dir($dir_full)) {
            ai_warning("unknown kernel module directory $kmod_dir");
        } else {
            foreach (scandir($dir_full) as $entry) {
                if ($entry == "." || $entry == "..") {
                    continue;
                }

                $entry_full = ai_join_paths($dir_full, $entry);
                if (is_file($entry_full) && preg_match('/^(.+)\.ko$/', $entry, $matches)) {
                    $modname = $matches[1];
                    ai_install_kmod($modname);
                }
                else if (is_dir($entry_full)) {
                    install_kmod_recurser(ai_join_paths($kmod_dir, $entry));
                }
            }
        }
    }

    // install directories of kernel modules
    foreach ($kmod_dirs as $kmod_dir) {
        install_kmod_recurser($kmod_dir);
    }

    // install modules.*
    foreach (scandir(ai_join_paths($ai_kmodroot, $ai_kmoddir)) as $name) {
        if (!preg_match('/^modules\..*$/', $name)) {
            continue;
        }
        $path = ai_join_paths($ai_kmoddir, $name);
        ai_install_fs_file($path, $target, $ai_kmodroot);
    }

    // install udev utility programs
    foreach (scandir(ai_join_paths($ai_root, $udev_dir)) as $name) {
        if (!preg_match('/^.*_id$/', $name)) {
            continue;
        }
        $path = ai_join_paths($udev_dir, $name);
        ai_install_fs_file($path);
    }

    // create newroot folder
    if (!mkdir(ai_join_paths($ai_out, "newroot"))) {
        ai_error("cannot mkdir newroot");
    }
}
